package org.amse.tanks.player;

import java.util.ArrayList;
import java.util.Random;

import org.amse.tanks.model.Model;
import org.amse.tanks.model.ModelConstants;
import org.amse.tanks.objects.Direction;
import org.amse.tanks.objects.Tank;

public class Bot implements Player, ModelConstants {
    private Tank myTank;
    private Model myModel;
    private ArrayList myMoves;
    
    public Bot(Model model, Tank tank) {
        myTank = tank;
        myModel = model;
        myModel.addTank(myTank);
        myMoves = new ArrayList();
    }

    public void getMove(String str) {
        getMove();
    }

    public void getMove() {
        makeStep();
    }

    public void makeStep() {
        Random randomGenerator = new Random();

        if (myTank.getMovingAbilityCounter() == 0) {
            if (myMoves.isEmpty()) {
                buildWay();
            } else {
                myModel.moveTank(myTank.getId(), (Direction) myMoves.remove(0));
            }
        }

        if (randomGenerator.nextInt(BOT_SHOOTING_PROBABILITY) == 0) {
            myModel.TankMakeShot(myTank.getId());
        }
    }

    private void buildWay() {
        Random randomGenerator = new Random();
        int finalX;
        int finalY;
        int tankX;
        int tankY;
        int currentX;
        int currentY;
        int currentWeight;
        int[][] weightsMap;
        int maxVal;
        int[][] queue;
        int queueHead;
        int queueTail;
        int x;
        int y;
        Direction direction;

        if (myMoves.isEmpty()) {
            do {
                finalX = randomGenerator.nextInt(myModel.getMapWidth());
                finalY = randomGenerator.nextInt(myModel.getMapHeight());
            } while (!myModel.isPositionAvailableForTank(finalX, finalY));

            tankX = myTank.getX();
            tankY = myTank.getY();

            weightsMap = new int[myModel.getMapWidth()][myModel.getMapHeight()];
            maxVal = weightsMap[0].length * weightsMap.length;
            for (int i = 0; i < weightsMap[0].length; i++) {
                for (int j = 0; j < weightsMap.length; j++) {
                    if (myModel.getMapValue(j, i) == MAP_FLOOR ||
                            myModel.getMapValue(j, i) == MAP_GRASS ||
                            myModel.getMapValue(j, i) == MAP_ICE) {
                        weightsMap[j][i] = maxVal;
                    } else {
                        weightsMap[j][i] = -1;
                    }
                }
            }

            weightsMap[tankX][tankY] = 0;

//            weightsMap[finalX][finalY] = 777;   //debug info
//            debugWayView(weightsMap);           //debug info

            queue = new int[maxVal][2];
            queue[0][0] = tankX;
            queue[0][1] = tankY;

            queueHead = 0;
            queueTail = 1;

            while (queueHead != queueTail) {
                currentX = queue[queueHead][0];
                currentY = queue[queueHead][1];
                currentWeight = weightsMap[currentX][currentY] + 1;

                if (currentX == finalX && currentY == finalY) {
                    break;
                }

                x = currentX - 1; //left
                y = currentY;
                if (myModel.isPositionAvailableForTank(x, y)) {
                    if (weightsMap[x][y] > currentWeight) {
                        weightsMap[x][y] = currentWeight;
                        queue[queueTail][0] = x;
                        queue[queueTail][1] = y;
                        queueTail++;
                    }
                }

                x = currentX; //up
                y = currentY - 1;
                if (myModel.isPositionAvailableForTank(x, y)) {
                    if (weightsMap[x][y] > currentWeight) {
                        weightsMap[x][y] = currentWeight;
                        queue[queueTail][0] = x;
                        queue[queueTail][1] = y;
                        queueTail++;
                    }
                }

                x = currentX + 1; //right
                y = currentY;
                if (myModel.isPositionAvailableForTank(x, y)) {
                    if (weightsMap[x][y] > currentWeight) {
                        weightsMap[x][y] = currentWeight;
                        queue[queueTail][0] = x;
                        queue[queueTail][1] = y;
                        queueTail++;
                    }
                }

                x = currentX; //down
                y = currentY + 1;
                if (myModel.isPositionAvailableForTank(x, y)) {
                    if (weightsMap[x][y] > currentWeight) {
                        weightsMap[x][y] = currentWeight;
                        queue[queueTail][0] = x;
                        queue[queueTail][1] = y;
                        queueTail++;
                    }
                }

                queueHead++;
            }

//            debugWayView(weightsMap);           //debug info

            if (queue[queueHead][0] == finalX && queue[queueHead][1] == finalY) { //if way exist
                x = finalX;
                y = finalY;
                currentWeight = weightsMap[finalX][finalY];
                
//                weightsMap[finalX][finalY] = 777;     //debug info

                while (x != tankX || y != tankY) {
                    direction = Direction.UP;
                    if (x > 0) {
                        if (currentWeight > weightsMap[x - 1][y    ] && weightsMap[x - 1][y    ] >= 0) {
                            direction = Direction.RIGHT;
                            currentWeight = weightsMap[x - 1][y    ];
                        }
                    }
                    if (y > 0) {
                        if (currentWeight > weightsMap[x    ][y - 1] && weightsMap[x    ][y - 1] >= 0) {
                            direction = Direction.DOWN;
                            currentWeight = weightsMap[x    ][y - 1];
                        }
                    }
                    if (currentWeight > weightsMap[x + 1][y    ] && weightsMap[x + 1][y    ] >= 0) {
                        direction = Direction.LEFT;
                        currentWeight = weightsMap[x + 1][y    ];
                    }
                    if (currentWeight > weightsMap[x    ][y + 1] && weightsMap[x    ][y + 1] >= 0) {
                        direction = Direction.UP;
                        currentWeight = weightsMap[x    ][y + 1];
                    }

                    switch (direction) {
                        case LEFT:
                            if (!myMoves.isEmpty()) {
                                if (Direction.LEFT != myMoves.get(0)) {
                                    myMoves.add(0, myMoves.get(0));
                                }
                            }
                            myMoves.add(0, Direction.LEFT);
                            x++;
                            break;

                        case UP:
                            if (!myMoves.isEmpty()) {
                                if (Direction.UP != myMoves.get(0)) {
                                    myMoves.add(0, myMoves.get(0));
                                }
                            }
                            myMoves.add(0, Direction.UP);
                            y++;
                            break;

                        case RIGHT:
                            if (!myMoves.isEmpty()) {
                                if (Direction.RIGHT != myMoves.get(0)) {
                                    myMoves.add(0, myMoves.get(0));
                                }
                            }
                            myMoves.add(0, Direction.RIGHT);
                            x--;
                            break;

                        case DOWN:
                            if (!myMoves.isEmpty()) {
                                if (Direction.DOWN != myMoves.get(0)) {
                                    myMoves.add(0, myMoves.get(0));
                                }
                            }
                            myMoves.add(0, Direction.DOWN);
                            y--;
                            break;
                    }
//                    weightsMap[x][y] = 777;     //debug info
                }

                if (!myMoves.isEmpty()) {
                    if (myTank.getDirection() != myMoves.get(0)) {
                        myMoves.add(0, myMoves.get(0));
                    }
                }
            }
//            debugWayView(weightsMap);           //debug info
        }
    }

//    private void debugWayView(int[][] weightsMap) {
//        for (int i = 0; i < weightsMap[0].length; i++) {
//            for (int j = 0; j < weightsMap.length; j++) {
//                if (weightsMap[j][i] >= -9 && weightsMap[j][i] < 0) {
//                    System.out.format("  %d", weightsMap[j][i]);
//                }
//                if (weightsMap[j][i] >= 0 && weightsMap[j][i] < 10) {
//                    System.out.format("   %d", weightsMap[j][i]);
//                }
//                if (weightsMap[j][i] >= 10 && weightsMap[j][i] < 100) {
//                    System.out.format("  %d", weightsMap[j][i]);
//                }
//                if (weightsMap[j][i] >= 100 && weightsMap[j][i] < 1000) {
//                    System.out.format(" %d", weightsMap[j][i]);
//                }
//            }
//            System.out.println();
//        }
//        System.out.println();
//    }
}
